package com.flexandair.templates
{
	import com.flexandair.view.components.*;
	
	import flash.filesystem.*;
	
	public class CodeFactory
	{
		private static var instance:CodeFactory;
		private static var allowInstantiation:Boolean = false;
		
		private var _voPath:String = "";
		private var _dbName:String = "";
		private var _dbUser:String = "root";
		private var _dbPass:String = "";
		private var _dbHost:String = "localhost";
		private var _objectArray:Array = new Array();
		private var _makeAS:Boolean = true;
		private var _outputDirectory:String = "";
		public var webURL:String = "";
		
		public function CodeFactory()
		{
			if (!allowInstantiation) 
		 	{
				throw new Error("Error: Instantiation failed: Use getInstance() instead of new.");
		  	}
		}

		public static function getInstance() : CodeFactory
		{
			if (instance == null) 
		 	{
				allowInstantiation = true;
				instance = new CodeFactory();
				allowInstantiation = false;
			}
			return instance;
		}
		
		public function get voPath():String 
		{
			return _voPath;
		}
		
		public function set voPath(input:String):void 
		{
			_voPath = input;
		}
		
		public function get dbHost():String 
		{
			return _dbHost;
		}
		
		public function set dbHost(input:String):void 
		{
			_dbHost = input;
		}
		
		public function get dbName():String 
		{
			return _dbName;
		}
		public function set dbName(input:String):void 
		{
			_dbName = input;
		}
		
		public function get dbUser():String 
		{
			return _dbUser;
		}
		
		public function set dbUser(input:String):void 
		{
			_dbUser = input;
		}
		
		public function get dbPass():String 
		{
			return _dbPass;
		}
		
		public function set dbPass(input:String):void 
		{
			_dbPass = input;
		} 
		
		public function get objectArray():Array 
		{	
			return _objectArray;
		}
		
		public function set objectArray(input:Array):void 
		{
			_objectArray = input;
		}
		
		public function get makeAS():Boolean 
		{
			return _makeAS;
		}
		
		public function set makeAS(input:Boolean):void 
		{
			_makeAS = input;
		}
		
		public function get outputDirectory():String 
		{
			return _outputDirectory;
		}
		
		public function set outputDirectory(input:String):void 
		{
			_outputDirectory = input;
		}
		
		//Code and file generation
		public function createFolders():void 
		{
			var baseDir:String = _outputDirectory;
			var includeDir:File = new File(baseDir + '/include/services');
			includeDir.createDirectory();
			var gatewayFile:File = new File();
			gatewayFile.nativePath = baseDir + "/gateway.php";
			var gwStream:FileStream = new FileStream();
			gwStream.open(gatewayFile, FileMode.WRITE);
			gwStream.writeUTFBytes(createGatewayFile(_objectArray));
			gwStream.close();
			var mxmlFile:File = new File();
			mxmlFile.nativePath = baseDir + "/sample_mxml.txt";
			var mxStream:FileStream = new FileStream();
			mxStream.open(mxmlFile, FileMode.WRITE);
			mxStream.writeUTFBytes(createMXTags(_objectArray));
			mxStream.close();			
			for each (var object:CustomObjectFile in _objectArray) {
				// create the class file, as in Contact.php
				var fileName:String = object.className;
				var classFile:File = new File();
				classFile.nativePath = baseDir + '/include/services/' + fileName + ".php";
				//Alert.show(baseDir + '/include/services/' + fileName + ".php", "File Path");
				var classStream:FileStream = new FileStream();
				classStream.open(classFile, FileMode.WRITE);
				classStream.writeUTFBytes(createClassFile(object));
				classStream.close();
				// create the services file, as in ContactService.php
				var servicesFile:File = new File(baseDir + '/include/services/' + fileName + "Service.php");
				var servicesStream:FileStream = new FileStream();
				servicesStream.open(servicesFile, FileMode.WRITE);
				servicesStream.writeUTFBytes(createServicesFile(object));
				servicesStream.close();	
				var voLocation:String = _voPath.replace(".","/");		
				var voFile:File = new File(baseDir + '/ActionScript/'+ voLocation +'/'+ object.className +'VO.as');
				var voStream:FileStream = new FileStream();
				voStream.open(voFile, FileMode.WRITE);
				voStream.writeUTFBytes(createVOFile(object));
				voStream.close();	
			}
		}
		
		private function createClassFile(object:CustomObjectFile):String {
			var fileContents:String = "<?php\n";
			fileContents += "class " + object.className + " {\n";
			for each (var prop:ObjectProperty in object.classProperties) {
				if (prop.dataType.toLowerCase() == 'string') {
					fileContents += "\tpublic $" + prop.name + " = '" + prop.initialValue + "';\n";
				}
				else if (prop.dataType.toLowerCase() == 'array') {
					fileContents += "\tpublic $" + prop.name + " = array();\n";
				}
				else {
					fileContents += "\tpublic $" + prop.name + " = " + prop.initialValue + ";\n";
				}
			}
			fileContents += "}\n";
			fileContents += "?>";
			return fileContents;
		}
		
		private function createVOFile(object:CustomObjectFile):String 
		{
			var fileContents:String = "package " + _voPath + "\n";
			fileContents += "{\n";
			fileContents += "\t[Bindable]\n";
			fileContents += '\t[RemoteClass(alias="'+ object.className +'VO")]\n\n';
			fileContents += "\tpublic class " + object.className + "VO \n";
			fileContents += "\t{\n";
			for each (var prop:ObjectProperty in object.classProperties) {
				if (prop.dataType.toLowerCase() == 'string' && prop.isClass == false) {
					fileContents += "\t\tpublic var " + prop.name + ":String = '" + prop.initialValue + "';\n";
				}
				if (prop.dataType.toLowerCase() == 'array' && prop.isClass == false) {
					fileContents += "\t\tpublic var " + prop.name + ":Array = new Array();\n";
				}
				if (prop.dataType.toLowerCase() == 'int' && prop.isClass == false) {
					fileContents += "\t\tpublic var " + prop.name + ":int = " + prop.initialValue + ";\n";
				}
				else {
					fileContents += "\t\tpublic var " + prop.name + " = " + prop.initialValue + ";\n";
				}
			}
			fileContents += "\n\t\tpublic function " + object.className + "VO()\n";
			fileContents += "\t\t{\n";
			fileContents += "\t\t}\n";
			fileContents += "\t}\n";
			fileContents += "}";
			return fileContents;
		}
		
		private function createServicesFile(object:CustomObjectFile):String 
		{	
			var fileContents:String = "<?php\n";
			fileContents += "include '"+ object.className +".php';\n";
			fileContents += "class "+ object.className +"Service {\n";
			for each (var method:String in object.remoteMethods) {
				if (method == 'create') {
					fileContents += "/**\n* save a new "+ object.className +"\n*\n* @param "+ object.className +" $" + object.className.toLowerCase() + "\n";
					fileContents += "* @return null\n";
					fileContents += "**/\n";
					fileContents += "\tfunction create" + object.className + "("+ object.className + " $"+ object.className.toLowerCase() +") {\n";
					for each (var prop:ObjectProperty in object.classProperties) {
						fileContents += "\t\t$" + prop.name + " = $" + object.className.toLowerCase() + "->" + prop.name + ";\n";
					}
					fileContents += "\t\t$dbh = mysql_connect('" + _dbHost + "', '" + _dbUser + "', '" + _dbPass + "');\n";
					fileContents += '\t\t$query = "INSERT INTO `' + _dbName + '`.`' + object._dbTable + '` (';
					var i:Number = object.classProperties.length - 1;
					var j:Number = 1;
					var propsList:String = "";
					var varsList:String = "";
					for each (var prop:ObjectProperty in object.classProperties) {
						if (j <= i) {
							propsList += prop.name + ', ';
							if (prop.name == 'id') {
								varsList += 'NULL, ';
							}
							else {
								varsList += '\'$' + prop.name + '\', ';
							}
						}
						if (j > i) {
							propsList += prop.name;
							if (prop.name == 'id') {
								varsList += 'NULL';
							}
							else {
								varsList += '\'$' + prop.name + '\'';
							}
						}
						j++;
					}
					fileContents += propsList + ') VALUES (' + varsList + ')";\n';
					fileContents += "\t\t$result = mysql_query($query, $dbh);\n";
					fileContents += "\t}\n";
				}
				if (method == 'retrieve') {
					fileContents += "/**\n* retrieve all "+ object.className +"s from the database \n*\n*\n";
					fileContents += "* @return array\n";
					fileContents += "**/\n";
					fileContents += "\tfunction retrieve" + object.className + "() {\n";
					fileContents += "\t\t$dbh = mysql_connect('" + _dbHost + "', '" + _dbUser + "', '" + _dbPass + "');\n";
					fileContents += "\t\t$query = 'SELECT * FROM `" + _dbName + "`.`" + object._dbTable + "`';\n";
					fileContents += "\t\t$result = mysql_query($query, $dbh);\n";
					fileContents += "\t\t$" + object.className.toLowerCase() + "Array = array();\n";
					fileContents += "\t\twhile ($row = mysql_fetch_array($result)) {\n";
					fileContents += "\t\t\t$" + object.className.toLowerCase() + " = new " + object.className + "();\n";
					for each (var prop:ObjectProperty in object.classProperties) {
						fileContents += "\t\t\t$" + object.className.toLowerCase() + "->" + prop.name + " = $row['" + prop.name + "'];\n";
					}
					fileContents += "\t\t\tarray_push($" + object.className.toLowerCase() + "Array, $" + object.className.toLowerCase() + ");\n";
					fileContents += "\t\t}\n";
					fileContents += "\t\treturn $" + object.className.toLowerCase() + "Array;\n";
					fileContents += "\t}\n"; 
				}
				if (method == 'update') {
					fileContents += "/**\n* update a "+ object.className +"\n*\n* @param "+ object.className +" $" + object.className.toLowerCase() + "\n";
					fileContents += "* @return null\n";
					fileContents += "**/\n";
					fileContents += "\tfunction update" + object.className + "(" + object.className + " $" + object.className.toLowerCase() + ") {\n";
					for each (var prop:ObjectProperty in object.classProperties) {
						fileContents += "\t\t$" + prop.name + " = $" + object.className.toLowerCase() + "->" + prop.name + ";\n";
					}
					fileContents += "\t\t$dbh = mysql_connect('" + _dbHost + "', '" + _dbUser + "', '" + _dbPass + "');\n";
					fileContents += "\t\t$query = \"UPDATE `" + _dbName + "`.`" + object._dbTable + "` SET ";
					var i:Number = object.classProperties.length - 1;
					var j:Number = 0;
					for each (var prop:ObjectProperty in object.classProperties) {
						if (prop.name == 'id') {
							
						}
						else {
							if (j < i) {
								fileContents += prop.name +" = \'$" + prop.name + "', ";
							}
							else {
								fileContents += prop.name +" = \'$" + prop.name + "' ";
							}
						}
						j++;
					}
					fileContents += "WHERE id = '$id'\";\n";
					fileContents += "\t\t$result = mysql_query($query, $dbh);\n";
					fileContents += "\t}\n";
				}
				if (method == 'delete') {
					fileContents += "/**\n* delete a "+ object.className +" from the database\n*\n* @param int $id\n";
					fileContents += "* @return null\n";
					fileContents += "**/\n";
					fileContents += "\tfunction delete" + object.className + "($id) {\n";
					fileContents += "\t\t$dbh = mysql_connect('" + _dbHost + "', '" + _dbUser + "', '" + _dbPass + "');\n";
					fileContents += "\t\t$query = \"DELETE FROM `" + _dbName + "`.`" + object._dbTable + "` WHERE id = $id LIMIT 1\";\n";
					fileContents += "\t\t$result = mysql_query($query,$dbh);\n";
					fileContents += "\t}\n";
				}
			}
			// code for create, retrieve, update, delete functions in PHP
			fileContents += "}\n?>";
			return fileContents;
		}
		
		private function createMXTags(objects:Array):String {
			var fileContents:String = "";
			for each (var object:CustomObjectFile in objects) {
				fileContents += '<mx:RemoteObject id="ro' + object.className + 'Service" ';
				fileContents += 'destination="zend" source="' + object.className + 'Service" endpoint="http://localhost/gateway.php">\n';
				for each (var method:String in object.remoteMethods) {
					fileContents += '\t<mx:method name="' + method + object.className + '" result="on' + method + object.className + 'Result(event)" fault="handleFault(event)"/>\n';
				}
				fileContents += '</mx:RemoteObject>\n';
			}
			return fileContents;
		}
		
		private function createGatewayFile(objects:Array):String {
			var fileContents:String = "";
			fileContents = "<?php\n";
			fileContents += "require_once 'Zend/Amf/Server.php';\n";
			for each (var object:CustomObjectFile in objects) {
				fileContents += "require_once 'include/services/" + object.className + "Service.php';\n";
			}
			fileContents += "// Instantiate the server\n";
			fileContents += "$server = new Zend_Amf_Server();\n";
			for each (var object:CustomObjectFile in objects) {
				fileContents += "$server->setClass('" + object.className + "Service');\n";
				fileContents += "$server->setClassMap('" + object.className + "VO', '" + object.className + "');\n";
			}
			fileContents += "$server->setProduction(false);\n";
			fileContents += "$response = $server->handle();\n";
			fileContents += "echo $response;\n";
			fileContents += "?>";
			return fileContents;
		}
	}
}